using	System;
using	System.Net; 
using	System.Web;
using	System.IO; 
using	System.Text;

using	System.Collections;
using	System.Text.RegularExpressions;
using	System.Threading;

//using	System.Windows.Forms;
using	My_HTTP;
using	Miner_Highlearn;
using Logging;
 
namespace Highlearner_API
{

	public delegate void SimpleDelegate();
	public delegate void DownloadStarted(Course a_oCourse, int a_iNumberOfItems);
	public delegate void DownloadEnded(Course a_oCourse);
	public delegate void FileDownloadDelegate(Course a_oCourse, FolderItem a_oFolder, FileItem a_oFile);
	public delegate void CourseUpdatedDelegate(String a_sCourseName);

	delegate bool GetCourseInformationDelegate(string a_sCourseName, bool a_bReGet);
	delegate bool ConnectDelegate();
	
	
	/// <summary>
	/// This guy is in charge of making it all happen. He connects, and displays the tree with the courses
	/// </summary>
	public class Executer {
		// Vars

		private bool m_bTerminated = false;
		private bool m_bAbortAction = false;

		private Mutex m_oCoreAccess = new Mutex();

	
		public			string						CONFIG_USERNAME;
		public			string						CONFIG_PASSWORD;
		public			string[]					COURSE_LIST;

	
		public			API_Highlearn				Core;

		public			CDataCenter					DataCenter;

		public bool Connected = false;
		// Whether to autoconnect when attempting downloads
		public bool AutoConnect = true;

		public string SavePath
		{
			get
			{
				m_oCoreAccess.WaitOne();

				try
				{
					return Core.SavePath;
				}
				finally
				{
					m_oCoreAccess.ReleaseMutex();
				}
			}
			set
			{
				m_oCoreAccess.WaitOne();

				try
				{
					Core.SavePath = value;
				}
				finally
				{
					m_oCoreAccess.ReleaseMutex();
				}
			}
		}

		#region Events

		// Events
		public event SimpleDelegate OnConnected;
		public event SimpleDelegate OnDisconnected;

		public event SimpleDelegate OnCoursesLoadedEvent;

		/// <summary>
		/// Fired when starting the download process of many files
		/// </summary>
		public event DownloadStarted OnDownloadStarted;
		/// <summary>
		/// Fired when download process of many files ended
		/// </summary>
		public event DownloadEnded OnDownloadEnded;

		public event FileDownloadDelegate OnFileDownloadStarted;
		public event FileDownloadDelegate OnFileDownloadEndedSuccessfully;
		public event FileDownloadDelegate OnFileDownloadFailed;

		public event CourseUpdatedDelegate OnCourseInformationRequest;
		public event CourseUpdatedDelegate OnCourseInformationUpdated;

		// Event firing methods (Are public for testing purposes)
		public void PerformOnDisconnected()
		{
			if (OnDisconnected != null)
			{
				OnDisconnected();
			}
		}
		public void PerformOnCoursesLoadedEvent()
		{
			if (OnCoursesLoadedEvent != null)
			{
				OnCoursesLoadedEvent();
			}
		}
		public void PerformOnFileDownloadStarted(Course a_oCourse, String a_sPath, String a_sFilename)
		{
			FolderItem l_oFolder = a_oCourse.GetFolderAt(a_sPath);
			PerformOnFileDownloadStarted(a_oCourse, l_oFolder, l_oFolder.Files[a_sFilename]);
		}
		public void PerformOnFileDownloadStarted(Course a_oCourse, FolderItem a_oFolder, FileItem a_oFile)
		{
			if (OnFileDownloadStarted != null)
			{
				OnFileDownloadStarted(a_oCourse, a_oFolder, a_oFile);
			}
		}
		public void PerformOnFileDownloadEndedSuccessfully(Course a_oCourse, String a_sPath, String a_sFilename)
		{
			CLogger.LogFileMessage(a_oCourse.Name, a_sPath, a_sFilename, "Downloaded Succesfully");
			FolderItem l_oFolder = a_oCourse.GetFolderAt(a_sPath);
			PerformOnFileDownloadEndedSuccessfully(a_oCourse, l_oFolder, l_oFolder.Files[a_sFilename]);
		}
		public void PerformOnFileDownloadEndedSuccessfully(Course a_oCourse, FolderItem a_oFolder, FileItem a_oFile)
		{
			if (OnFileDownloadEndedSuccessfully != null)
			{
				OnFileDownloadEndedSuccessfully(a_oCourse, a_oFolder, a_oFile);
			}
		}
		public void PerformOnFileDownloadFailed(Course a_oCourse, String a_sPath, String a_sFilename)
		{
			CLogger.LogFileMessage(a_oCourse.Name, a_sPath, a_sFilename, "File Failed");
			FolderItem l_oFolder = a_oCourse.GetFolderAt(a_sPath);
			PerformOnFileDownloadFailed(a_oCourse, l_oFolder, l_oFolder.Files[a_sFilename]);
		}
		public void PerformOnFileDownloadFailed(Course a_oCourse, FolderItem a_oFolder, FileItem a_oFile)
		{
			if (OnFileDownloadFailed != null)
			{
				OnFileDownloadFailed(a_oCourse, a_oFolder, a_oFile);
			}
		}

		#endregion

		// Public Methods
		public Executer() : base()
		{
			DataCenter = new CDataCenter();
		}

		private bool Aborted()
		{
			if ((m_bAbortAction) || (m_bTerminated))
			{
				m_bAbortAction = false;
				return true;
			}

			return false;
		}

		public bool Connect(bool a_bAsync)
		{
			if (a_bAsync)
			{
				ConnectDelegate l_oCaller = new ConnectDelegate(Connect);
				l_oCaller.BeginInvoke(null, null);
				return false;
			}

			return Connect();
		}

		public bool Connect()
		{
			CLogger.LogFunctionEntry();

			if (!Connected)
			{
				try
				{
					m_oCoreAccess.WaitOne();

					CLogger.LogFunctionEntry("Logging Out...");
					Core.Logout();

					CLogger.LogFunctionEntry("Logging In...");
					Connected = Core.Login(CONFIG_USERNAME, CONFIG_PASSWORD);



				}
				catch (Exception e)
				{
					CLogger.LogFunctionEntry("Connection Failed");
				}
				m_oCoreAccess.ReleaseMutex();

				if (Connected)
				{
					if (OnConnected != null)
					{
						OnConnected();
					}
				}
				else
				{
					PerformOnDisconnected();
				}
			}

			CLogger.LogFunctionEntry("Done");
			return Connected;
		}

		public bool Disconnect()
		{
			CLogger.LogFunctionEntry();
			if (Connected)
			{
				CLogger.LogFunctionEntry("Logging Out...");
				Core.Logout();
				Connected = false;

				// Stop all queued actions
				Terminate();
				m_oCoreAccess.WaitOne();
				m_oCoreAccess.ReleaseMutex();
				m_bTerminated = false;

				// Notify
				PerformOnDisconnected();
			}

			CLogger.LogFunctionEntry("Done");
			return true;
		}
			
		public void GetAvailibleCourses(bool a_bAsync)
		{
			if (a_bAsync)
			{
				new Thread(GetAvailibleCourses).Start();
				return;
			}

			GetAvailibleCourses();
		}

		public void GetAvailibleCourses()
		{
			CLogger.LogFunctionEntry();
			if ((Connected) || ((!Connected) && (AutoConnect)))
			{
				CLogger.LogFunctionEntry("Connected");
				if (Aborted())
				{
					return;
				}

				try
				{
					m_oCoreAccess.WaitOne();

					if (Aborted())
					{
						return;
					}


					if (Connect())
					{
						Core.Get_Course_List();

						if (Aborted())
						{
							return;
						}

						DataCenter.Parse(Core.hash_Folders);
					}
				}
				finally
				{
					m_oCoreAccess.ReleaseMutex();
				}

				if (Connected)
				{
					PerformOnCoursesLoadedEvent();
				}
			}
			CLogger.LogFunctionEntry("Done");
		}

		public void DownloadAll(bool a_bAsync)
		{
			if (a_bAsync)
			{
				new Thread(DownloadAll).Start();
				return;
			}

			GetAvailibleCourses();
		}

		public void DownloadAll()
		{
			foreach (Course l_oCourse in DataCenter.Courses.Values)
			{
				if (Aborted())
				{
					return;
				}

				if (l_oCourse.Download)
				{
					DownloadCourseItems(l_oCourse);
				}
			}
		}

		public void DownloadCourseItems(Course a_oCourse)
		{
			string l_sLinkToMoreResults = "";
			int l_iRetryCount;
			// Notify the download start
			if (OnDownloadStarted != null)
			{
				OnDownloadStarted(a_oCourse, a_oCourse.CountFiles(true));
			}

			if (!(a_oCourse.DataRetrieved == ERetrievedState.Retrieved))
			{
				GetCourseInformation(a_oCourse.Name, true);
			}


			// The API-Highlearn will add skipped items here
			Core.v_items_skipped.Clear();

			int l_iCourseIndex = a_oCourse.Index;

			if (Aborted())
			{
				return;
			}

			try
			{
				m_oCoreAccess.WaitOne();

				if (Aborted())
				{
					return;
				}

				// Loop through all search result pages
				do
				{
					l_sLinkToMoreResults = Core.Get_Course_Items(l_iCourseIndex, l_sLinkToMoreResults);
					for (int i = 0; i < Core.current_page_count_items; i++)
					{
						l_iRetryCount = 1;

						try
						{
							// Download the link only if it is selected in the tree;
							if (a_oCourse.GetFolderAt(Core.v_item_path[i]).Files[Core.v_item_names[i]].Download)
							{
								m_oCoreAccess.ReleaseMutex();

								PerformOnFileDownloadStarted(a_oCourse, Core.v_item_path[i], Core.v_item_names[i]);

								m_oCoreAccess.WaitOne();

								while (l_iRetryCount > 0)
								{
									// Find download link to the item
									if (Core.Request_Course_Item(i))
									{
										if (Aborted())
										{
											return;
										}

										Core.Download_Course_Item_Requested(false);

										l_iRetryCount = 0;

										m_oCoreAccess.ReleaseMutex();

										// Notify
										PerformOnFileDownloadEndedSuccessfully(
											a_oCourse,
											Core.v_item_path[i],
											Core.v_item_names[i]);

										m_oCoreAccess.WaitOne();
									}
									else
									// Download Failed
									{
										if (Aborted())
										{
											return;
										}

										l_iRetryCount -= 1;

										if (l_iRetryCount > 0)
										{
											Core.logger.Step_In("*** Unable to download item, trying again");
											Core.Logout();
											Core.Login(CONFIG_USERNAME, CONFIG_PASSWORD);
											Core.logger.Step_Out();
										}
									}
								}
							}
						}
						catch (Exception e)
						{
							Core.logger.WriteLn(Core.v_item_path[i] + " Ignored. Error: " + e.Message);

							PerformOnFileDownloadFailed(a_oCourse, Core.v_item_path[i], Core.v_item_names[i]);
						}
					}
				} while (l_sLinkToMoreResults != "");

				m_oCoreAccess.ReleaseMutex();
			}
			catch (Exception l_oException)
			{
				m_oCoreAccess.ReleaseMutex();

				CLogger.ReportException(l_oException);
				throw l_oException;
			}

			if (OnDownloadEnded != null)
			{
				OnDownloadEnded(a_oCourse);
			}

/*			}
			catch (ThreadAbortException e) {
				core.logger.loglevel = 0;
				core.logger.WriteLn("\""+e.Message+"\"",0);
				return;
			}
			catch (WebException e) {
				if(e.Status == WebExceptionStatus.NameResolutionFailure)
					core.logger.WriteLn("Error: "+core.server_prefix+" is unreachable!",0);
				else {
					core.logger.WriteLn("Error in: "+e.TargetSite.Name,0);
					core.logger.WriteLn("\t\""+e.Message+"\"",0);
				}
			}
			catch (Exception e) {
				core.logger.WriteLn("Error in: "+e.TargetSite.Name,0);
				core.logger.WriteLn("\t\""+e.Message+"\"",0);
			}*/
		}

		public void GetCourseInformation(string a_sCourseName, bool a_bReGet, bool a_bAsync)
		{
			if (a_bAsync)
			{
				GetCourseInformationDelegate l_oCaller = new GetCourseInformationDelegate(GetCourseInformation);
				l_oCaller.BeginInvoke(a_sCourseName, a_bReGet, null, null);
				return;
			}

			GetCourseInformation(a_sCourseName, a_bReGet);
		}		

		/// <summary>
		/// Downloads the course information
		/// </summary>
		/// <param name="a_sCourseName">Self-Explainatory</param>
		/// <param name="a_bReGet">If the course has already been downloaded, should it be downloaded again?</param>
		/// <returns></returns>
		public bool GetCourseInformation(string a_sCourseName, bool a_bReGet)
		{
			CLogger.LogFunctionEntry(a_sCourseName, a_bReGet);

			if ((DataCenter.Courses[a_sCourseName].DataRetrieved == ERetrievedState.Retrieved) && (!a_bReGet))
			{
				return false;
			}

			try
			{
				if (Aborted())
				{
					return false;
				}

				// Notify start of request
				if (OnCourseInformationRequest != null)
				{
					OnCourseInformationRequest(a_sCourseName);
				}

				if (Aborted())
				{
					return false;
				}

				m_oCoreAccess.WaitOne();
				
				Core.GetCourseItemsWithPages(DataCenter.Courses[a_sCourseName].Index);

				if (Aborted())
				{
					return false;
				}

				// Fill the tree with the courses
				DataCenter.ParseFolders(a_sCourseName, Core.HLLastItems);
			}
			finally
			{
				m_oCoreAccess.ReleaseMutex();
			}

			if (OnCourseInformationUpdated != null)
			{
				OnCourseInformationUpdated(a_sCourseName);
			}

			return true;
		}

		public void AbortCurrentAction()
		{
			m_bAbortAction = true;
		}

		public void Terminate()
		{
			m_bTerminated = true;
		}		
	}
}